import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'entity.dart';
import 'thumbnail.dart';

const ThumbnailSize vDefaultGridThumbnailSize = ThumbnailSize.square(200);

/// Common file types for images.
enum ImageFileType { jpg, png, gif, tiff, heic, other }

/// {@template photo_manager.AssetType}
/// The type of the asset.
///
/// Most of assets are [image] and [video],
/// some assets might be [audio] on Android.
/// The [other] type won't show in general.
/// {@endtemplate}
///
/// **IMPORTANT FOR MAINTAINERS:** **DO NOT** change orders of values.
enum AssetType {
  /// Assets other than [image], [video] and [audio].
  other,
  image,
  video,
  audio,
}

@immutable
class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {
  const AssetEntityImageProvider(
    this.entity,
    this.data, {
    this.width,
    this.height,
    this.thumbnailFormat = ThumbnailFormat.jpeg,
  });

  final AssetEntity entity;

  final Uint8List data;
  final double width;
  final double height;

  final ThumbnailFormat thumbnailFormat;

  @override
  ImageStreamCompleter load(
    AssetEntityImageProvider key,
    DecoderCallback decode,
  ) {
    return MultiFrameImageStreamCompleter(
      codec: _loadAsync(key, decode),
      scale: 1.0,
      informationCollector: () {
        return <DiagnosticsNode>[
          DiagnosticsProperty<ImageProvider>('Image provider', this),
          DiagnosticsProperty<AssetEntityImageProvider>('Image key', key),
        ];
      },
    );
  }

  @override
  Future<AssetEntityImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<AssetEntityImageProvider>(this);
  }

  Future<ui.Codec> _loadAsync(
    AssetEntityImageProvider key,
    DecoderCallback decode,
  ) async {
    try {
      assert(key == this);
      if (key.entity.type == AssetType.audio ||
          key.entity.type == AssetType.other) {
        throw UnsupportedError(
          'Image data for the ${key.entity.type} is not supported.',
        );
      }
      return decode(data,
          cacheHeight: (height ?? 200).toInt(),
          cacheWidth: (width ?? 200).toInt());
    } catch (e) {
      Future<void>.microtask(() {
        PaintingBinding.instance?.imageCache?.evict(key);
      });
      rethrow;
    }
  }
}
